package java_thinking.unit_21.concurrentutil;

/**
 * @Desc:   AQS 实现原理 https://blog.csdn.net/w_s_h_y/article/details/77450166 必看
 *          1.在AQS同步器中维护着一个同步队列，当线程获取同步状态失败后，将会被封装成Node结点，加入到同步队列中并进行自旋操
 *            作，当前线程结点的前驱结点为head时，将尝试获取同步状态，获取成功将自己设置为head结点。在释放同步状态时，则通
 *            过调用子类(ReentrantLock中的Sync内部类)的tryRelease(int releases)方法释放同步状态，释放成功则唤醒后继结点的
 *            线程。
 *
 *          2.AQS内部通过内部类Node构成FIFO的同步队列来完成线程获取锁的排队工作，同时利用内部类ConditionObject构建等待队列，
 *            当Condition调用wait()方法后，线程将会加入等待队列中，而当Condition调用signal()方法后，线程将从等待队列转移动
 *            同步队列中进行锁竞争。注意这里涉及到两种队列，一种的同步队列，当线程请求锁而等待的后将加入同步队列等待，而另一
 *            种则是等待队列(可有多个)，通过Condition调用await()方法释放锁后，将加入等待队列。
 *
 *          3.从上述类图可以看出AQS是一个抽象类，但请注意其源码中并没一个抽象的方法，这是因为AQS只是作为一个基础组件，并不希
 *            望直接作为直接操作类对外输出，而更倾向于作为基础组件，为真正的实现类提供基础设施，如构建同步队列，控制同步状态
 *            等，事实上，从设计模式角度来看，AQS采用的模板模式的方式构建的，其内部除了提供并发操作核心方法以及同步队列操作外
 *            ，还提供了一些模板方法让子类自己实现，如加锁操作以及解锁操作，为什么这么做？这是因为AQS作为基础组件，封装的是核
 *            心并发操作，但是实现上分为两种模式，即共享模式与独占模式，而这两种模式的加锁与解锁实现方式是不一样的，但AQS只关
 *            注内部公共方法实现并不关心外部不同模式的实现，所以提供了模板方法给子类使用，也就是说实现独占锁，如ReentrantLock
 *            需要自己实现tryAcquire()方法和tryRelease()方法，而实现共享模式的Semaphore，则需要实现tryAcquireShared()方法
 *            和tryReleaseShared()方法，这样做的好处是显而易见的，无论是共享模式还是独占模式，其基础的实现都是同一套组件(AQS)
 *            ，只不过是加锁解锁的逻辑不同罢了，更重要的是如果我们需要自定义锁的话，也变得非常简单，只需要选择不同的模式实现不
 *            同的加锁和解锁的模板方法即可
 *
 *          4.重入锁 ReentrantLock，是一个基于AQS并发框架的并发控制类，其内部实现了3个类，分别是Sync、NoFairSync以及FairSync
 *            类，其中Sync继承自AQS，实现了释放锁的模板方法tryRelease(int)，而NoFairSync和FairSync都继承自Sync，实现各种获
 *            取锁的方法tryAcquire(int)。ReentrantLock的所有方法实现几乎都间接调用了这3个类，因此当我们在使用ReentrantLock
 *            时，大部分使用都是在间接调用AQS同步器中的方法，这就是ReentrantLock的内部实现原理,最后给出张类图结构
 *
 *              常见的面试题
 *              说明一下ReentrantLock的原理。
 *              ReentrantLock为什么是可重入的。
 *              公平锁和非公平锁是什么？有什么区别。
 *
 *          5.面试官：synchronized 和 ReentrantLock 有什么区别？
 *              答：synchronized 是 java 语言层面提供的一个关键字，而 ReentrantLock 是 java 的一个类，此外，ReentrantLock
 *              它可以用 Condition 对象创建多个等待队列，而 synchronized 锁定的对象只有一个等待队列。ReentrantLock比较灵活，
 *              可以响应中断，而synchronized里的代码要么是发生异常了要么是全部执行完才能退出，ReentrantLock支持等待一定的时间。
 *
 *          6.ReentrantReadWriteLock
 *            1 sharedCount不为 0 表示分配了读锁;
 *            2 exclusiveCount 不为 0 表示分配了写锁
 *            3 sharedCount和exclusiveCount 一般不会同时不为 0，只有当线程占用了写锁，该线程可以重入获取读锁，反之不成立
 *            4 在分析WriteLock和ReadLock的互斥性时可以按照WriteLock与WriteLock之间，WriteLock与ReadLock之间以及ReadLock
 *              与ReadLock之间进行分析。
 *            5 写锁是独占式锁\读锁是共享式锁
 *            6 Sync 实现的尝试获取锁
 *              在以下几种情况，获取读锁会失败：
 *              (1)有线程持有写锁，且该线程不是当前线程，获取锁失败。
 *              (2)写锁空闲 且 公平策略决定 读线程应当被阻塞，除了重入获取，其他获取锁失败。
 *              (3)读锁数量达到最多，抛出异常。
 *            7 在以下情况，写锁获取失败：
 *              (1)写锁为0，读锁不为0 或者写锁不为0，且当前线程不是已获取独占锁的线程，锁获取失败。
 *              (2)写锁数量已达到最大值，写锁获取失败。
 *              (3)当前线程应该阻塞，或者设置同步状态state失败，获取锁失败
 *
 *           8. 总结  https://www.cnblogs.com/konck/p/9473615.html 必看
 *              (1)首先说一下公平锁和非公平锁的区别，
 *                  公平锁：当线程发现已经有线程在排队获取锁了，那么它必须排队，除了一种情况就是，线程已经占有锁，此次是重入
 *                  ，不用排队。
 *                  非公平锁：只有一种情况需排队，其他情况不用排队就可以尝试获取锁： 如果当前全局处于读锁状态，且等待队列中
 *                  第一个等待线程想获取写锁，那么当前线程能够获取到读锁的条件为：当前线程获取了写锁，还未释放；当前线程获取
 *                  了读锁，这一次只是重入读锁而已；其它情况当前线程入队尾。
 *              (2)获取读锁和释放读锁
 *                  获取锁的过程：
 *                      当线程调用 acquireShared() 申请获取锁资源时，如果成功，则进入临界区。
 *                      当获取锁失败时，则创建一个共享类型的节点并进入一个FIFO等待队列，然后被挂起等待唤醒。
 *                      当队列中的等待线程被唤醒以后就重新尝试获取锁资源，如果成功则唤醒后面还在等待的共享节点并把该唤醒事
 *                      件传递下去，即会依次唤醒在该节点后面的所有共享节点，然后进入临界区，否则继续挂起等待。
 *                  释放锁过程：
 *                      当线程调用releaseShared()进行锁资源释放时，如果释放成功，则唤醒队列中等待的节点，如果有的话。
 *              (3)跟独占锁相比，共享锁的主要特征在于当一个在等待队列中的共享节点成功获取到锁以后(它获取到的是共享锁)，既
 *                 然是共享，那它必须要依次唤醒后面所有可以跟它一起共享当前锁资源的节点，毫无疑问，这些节点必须也是在等待共享
 *                 锁(这是大前提，如果等待的是独占锁，那前面已经有一个共享节点获取锁了，它肯定是获取不到的)。当共享锁被释放的
 *                 时候，可以用读写锁为例进行思考，当一个读锁被释放，此时不论是读锁还是写锁都是可以竞争资源的。
 *                 另外一点就是请求锁资源的时候共享锁会先请求是否有资源，没有资源才进入同步队列，而独享锁则直接进入队列
 *
 *           9.每个Condition都对应着一个等待队列，也就是说如果一个锁上创建了多个Condition对象，那么也就存在多个等待队列。
 *             等待队列是一个FIFO的队列，在队列中每一个节点都包含了一个线程的引用，而该线程就是Condition对象上等待的线程。
 *             当一个线程调用了await()相关的方法，那么该线程将会释放锁，并构建一个Node节点封装当前线程的相关信息加入到等待
 *             队列中进行等待，直到被唤醒、中断、超时才从队列中移出
 *
 * @author: cww
 * @DateTime: 2019/7/8 8:30
 */

public class AQSTheory {

}
